function varargout = TrialCleanerGUI(varargin) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Performs ICA decomposition and component removal. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Copyright (C) 2013-2014, Michael J. Cheung % % This file is a part of the MEG & PLS Pipeline (MEGPLS). For more % details, see the documentation included with the software package. % % MEGPLS is free software: you can redistribute it and/or modify it under % the terms of the GNU General Public License version 2 as published by % the Free Software Foundation. This program is distributed in the hope % that it will be useful, but WITHOUT ANY WARRANTY; without even the % implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % See the GNU General Public License for more details. % % You should have received a copy of the GNU General Public License along % with this program. If not, you can download the license here: % . % Last Modified by GUIDE v2.5 04-Sep-2014 18:30:53 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; gui_State = struct('gui_Name', mfilename, ... 'gui_Singleton', gui_Singleton, ... 'gui_OpeningFcn', @TrialCleanerGUI_OpeningFcn, ... 'gui_OutputFcn', @TrialCleanerGUI_OutputFcn, ... 'gui_LayoutFcn', [] , ... 'gui_Callback', []); if nargin && ischar(varargin{1}) gui_State.gui_Callback = str2func(varargin{1}); end if nargout [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); else gui_mainfcn(gui_State, varargin{:}); end % End initialization code - DO NOT EDIT %--- Executes just before TrialCleanerGUI is made visible. ---% %-----------------------------------------------------% function TrialCleanerGUI_OpeningFcn(hObject, eventdata, handles, varargin) % This function has no output args, see OutputFcn. % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % varargin command line arguments to TrialCleanerGUI (see VARARGIN) % Choose default command line output for TrialCleanerGUI handles.output = hObject; % Make sure toolbox paths are added: [PipelineDir, ~, ~] = fileparts(which('TrialCleanerGUI.m')); handles.PipelineDir = PipelineDir; addpath(genpath(PipelineDir)); rmpath([PipelineDir,'/DEFAULT_SETTINGS']); % Make sure its calling from DataID rmpath([PipelineDir,'/TEMPORARY_FIXES']); % Make sure its calling from FT toolbox CheckToolboxPaths(PipelineDir); if exist('ErrorLog_TrialCleaner.txt', 'file') system('rm ErrorLog_TrialCleaner.txt'); end % Initializes variables being displayed: handles.paths.Rootpath = []; handles.paths.DataID = []; handles.name.DataID = []; handles.name.GroupID = []; handles.name.SubjID = []; handles.name.CondID = []; handles.paths.InputPreprocMEG = []; handles.paths.BackupOrigFile = []; handles.gui.InpOutStatus = []; handles.FTcfg = []; % Save handles: guidata(hObject, handles); % UIWAIT makes TrialCleanerGUI wait for user response (see UIRESUME) % uiwait(handles.figure1); %--- Outputs from this function are returned to the command line. ---% %--------------------------------------------------------------------% function varargout = TrialCleanerGUI_OutputFcn(hObject, eventdata, handles) % varargout cell array for returning output args (see VARARGOUT); % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Get default command line output from handles structure varargout{1} = handles.output; %===================================% % FUNCTIONS FOR SELECTING ROOTPATH: % %===================================% %--- Executes on button press in ButtonSetRootpath. ---% %------------------------------------------------------% function ButtonSetRootpath_Callback(hObject, eventdata, handles) if ~isempty(handles.name.DataID) prompt = {'WARNING:'; ''; 'Changing root directory will clear all current settings!'; ''; 'Do you wish to continue?'}; ChangeDir = questdlg(prompt, 'WARNING:', 'YES', 'NO', 'NO'); if strcmp(ChangeDir, 'NO') return; end end % Select and set Rootpath: SelectedPath = uigetdir; if SelectedPath == 0 return % If user cancels end handles.paths.Rootpath = SelectedPath; set(handles.TextboxRootpath, 'String', handles.paths.Rootpath); % Start Waitbox: WaitBox = StartWaitBox('SETTING ROOT DIRECTORY:'); % Reset DataID: handles.name.DataID = []; handles.paths.DataID = []; set(handles.TextboxDataID, 'String', 'Not Selected.'); % Reset settings & GUI panels: handles = ResetGroupIDSettings (handles); handles = ResetSubjIDSettings (handles); handles = ResetCondIDSettings (handles); handles = ResetStatusSettings (handles); handles.paths.InputPreprocMEG = []; handles.paths.BackupOrigFile = []; % Update settings & GUI panels (to enable/disable): % Note: Don't redetect or compile paths yet since no DataID yet. handles = UpdateGroupIDSettings (handles); handles = UpdateSubjIDSettings (handles); handles = UpdateCondIDSettings (handles); handles = UpdateStatusSettings (handles); % Save handles: guidata(hObject, handles); close(WaitBox); %--- Textbox to display selected Rootpath: ---% %---------------------------------------------% function TextboxRootpath_Callback(hObject, eventdata, handles) EnteredText = get(handles.TextboxRootpath, 'String'); if ~isequal(EnteredText, handles.paths.Rootpath) set(handles.TextboxRootpath, 'String', handles.paths.Rootpath); msgbox('Note: Use button to change Rootpath.') end %=========================================% % FUNCTIONS FOR READING & LOADING DATAID: % %=========================================% %--- Textbox to display current DataID: ---% %------------------------------------------% function TextboxDataID_Callback(hObject, eventdata, handles) EnteredText = get(handles.TextboxDataID, 'String'); if ~isequal(EnteredText, handles.name.DataID) set(handles.TextboxDataID, 'String', handles.name.DataID); msgbox('Note: Use button to change DataID.') end %--- Executes on button press in ButtonLoadDataID. ---% %-----------------------------------------------------% function ButtonLoadDataID_Callback(hObject, eventdata, handles) if isempty(handles.paths.Rootpath) msgbox('Warning: Select target directory first.', 'Warning:'); return; end % Get DataID folders in current rootpath: DetectedFolders = dir([handles.paths.Rootpath,'/DataID_*']); DetectedFolders = [DetectedFolders; dir([handles.paths.Rootpath,'/DataID-ICAclean_*'])]; RemoveIndex = []; for d = 1:length(DetectedFolders) if DetectedFolders(d).isdir ~= 1 RemoveIndex = [RemoveIndex, d]; % Get unwanted indices end end DetectedFolders(RemoveIndex) = []; if isempty(DetectedFolders) msgbox('Error: No DataID folders detected inside target directory.', 'Error:'); return; end % List DataID folders for selection: DetectedFolders = {DetectedFolders.name}; SelectedIndex = listdlg('PromptString', 'Select DataID to load:',... 'ListSize', [300, 300], 'SelectionMode', 'single', 'ListString', DetectedFolders); if isempty(SelectedIndex) % If user cancels. return; end SelectedDataID = DetectedFolders{SelectedIndex}; if strcmp(SelectedDataID(1:7), 'DataID_') SelectedDataID(1:7) = []; % Remove "DataID_" prefix to get DataID tag. FullpathDataID = [handles.paths.Rootpath,'/DataID_',SelectedDataID]; elseif strcmp(SelectedDataID(1:16), 'DataID-ICAclean_') SelectedDataID(1:16) = []; % Remove "DataID-ICAclean_" prefix to get DataID tag. FullpathDataID = [handles.paths.Rootpath,'/DataID-ICAclean_',SelectedDataID]; end % Check or create settings .m file for ft_rejectvisual: if ~exist([FullpathDataID,'/SETTINGS'], 'dir') success = mkdir([FullpathDataID,'/SETTINGS']); if success == 0 message = {'ERROR:'; ''; 'Failed to create SETTINGS folder in selected DataID.'; 'Ensure you have proper folder and file permissions.'}; msgbox(message, 'ERROR:') return; end end DefaultSettingsFile = [handles.PipelineDir,'/DEFAULT_SETTINGS/Settings_RejectVisual.m']; NewSettingsFile = [FullpathDataID,'/SETTINGS/Settings_RejectVisual.m']; if ~exist(NewSettingsFile, 'file') status = copyfile(DefaultSettingsFile, NewSettingsFile, 'f'); if status == 0 message = {'ERROR:'; ''; 'Failed to create "ft_rejectvisual" settings file for selected DataID.'; 'Could not copy file from [MEG]PLS "DEFAULT_SETTINGS" directory.'; 'Check for proper folder and file permissions.'}; msgbox(message, 'ERROR:') return; end end % Set as current DataID: handles.name.DataID = SelectedDataID; handles.paths.DataID = FullpathDataID; % Reset settings & GUI panels: handles = ResetGroupIDSettings (handles); handles = ResetSubjIDSettings (handles); handles = ResetCondIDSettings (handles); handles = ResetStatusSettings (handles); handles.paths.InputPreprocMEG = []; handles.paths.BackupOrigFile = []; % Compile Group, Subj, and CondIDs for DataID: % Note: Also initializes Group, Subj, CondIDs. handles = DetectGroupSubjCond(handles); if isempty(handles.name.GroupID) msgbox('ERROR: No GroupID folders detected for selected DataID.') return; end % Compile paths & initialize other variables: for g = 1:length(handles.name.GroupID) for s = 1:length(handles.name.SubjID{g}) for c = 1:length(handles.name.CondID) handles = CompilePaths(handles, g, s, c); try handles.gui.InpOutStatus{g}{s,c}; catch handles.gui.InpOutStatus{g}{s,c} = []; end end end end % Update settings & GUI panels: handles = UpdateGroupIDSettings (handles); handles = UpdateSubjIDSettings (handles); handles = UpdateCondIDSettings (handles); % Detect & update status: for g = 1:length(handles.name.GroupID) StatusWaitbar = waitbar(0, ['Please wait. Scanning status for: ', ... handles.name.GroupID{g}], 'Windowstyle', 'modal'); for s = 1:length(handles.name.SubjID{g}) waitbar(s/length(handles.name.SubjID{g}), StatusWaitbar); for c = 1:length(handles.name.CondID) handles = DetectStatus(handles, g, s, c); end end delete(StatusWaitbar) end handles = UpdateStatusSettings(handles); % Save handles: set(handles.TextboxDataID, 'String', handles.name.DataID); guidata(hObject, handles); % Remove ErrorLog if empty: if exist([pwd,'/ErrorLog_TrialCleaner.txt'], 'file') LogCheck = dir('ErrorLog_TrialCleaner.txt'); if LogCheck.bytes == 0 delete('ErrorLog_TrialCleaner.txt') end end %--- Executes on button press in ButtonRedetectInputIDs. --% %----------------------------------------------------------% function ButtonRedetectInputIDs_Callback(hObject, eventdata, handles) % Reset settings & GUI panels: handles = ResetGroupIDSettings (handles); handles = ResetSubjIDSettings (handles); handles = ResetCondIDSettings (handles); handles = ResetStatusSettings (handles); handles.paths.InputPreprocMEG = []; handles.paths.BackupOrigFile = []; % Compile Group, Subj, and CondIDs for DataID: % Note: Also initializes Group, Subj, CondIDs. handles = DetectGroupSubjCond(handles); if isempty(handles.name.GroupID) msgbox('ERROR: No GroupID folders detected for selected DataID.') return; end % Compile paths & initialize other variables: for g = 1:length(handles.name.GroupID) for s = 1:length(handles.name.SubjID{g}) for c = 1:length(handles.name.CondID) handles = CompilePaths(handles, g, s, c); try handles.gui.InpOutStatus{g}{s,c}; catch handles.gui.InpOutStatus{g}{s,c} = []; end end end end % Update settings & GUI panels: handles = UpdateGroupIDSettings (handles); handles = UpdateSubjIDSettings (handles); handles = UpdateCondIDSettings (handles); % Detect & update status: for g = 1:length(handles.name.GroupID) StatusWaitbar = waitbar(0, ['Please wait. Scanning status for: ', ... handles.name.GroupID{g}], 'Windowstyle', 'modal'); for s = 1:length(handles.name.SubjID{g}) waitbar(s/length(handles.name.SubjID{g}), StatusWaitbar); for c = 1:length(handles.name.CondID) handles = DetectStatus(handles, g, s, c); end end delete(StatusWaitbar) end handles = UpdateStatusSettings(handles); % Save handles: guidata(hObject, handles); % Remove ErrorLog if empty: if exist([pwd,'/ErrorLog_TrialCleaner.txt'], 'file') LogCheck = dir('ErrorLog_TrialCleaner.txt'); if LogCheck.bytes == 0 delete('ErrorLog_TrialCleaner.txt') end end %--- Detect Group, Subj, and CondID's from DataID: ---% %-----------------------------------------------------% function OutputHandles = DetectGroupSubjCond(InputHandles) handles = InputHandles; % Reset params: handles.name.GroupID = []; handles.name.SubjID = []; handles.name.CondID = []; handles.paths.InputPreprocMEG = []; handles.paths.BackupOrigFile = []; % Acquire GroupID folders within DataID: DetectedGroups = dir([handles.paths.DataID,'/GroupID_*']); RemoveIndex = []; for g = 1:length(DetectedGroups) DetectedGroups(g).name(1:8) = []; % Remove "GroupID_" prefix if DetectedGroups(g).isdir ~= 1 RemoveIndex = [RemoveIndex, g]; % Get unwanted indices end end DetectedGroups(RemoveIndex) = []; if isempty(DetectedGroups) handles.name.GroupID = []; else handles.name.GroupID = {DetectedGroups.name}; end % Scan PreprocInput .mat files for SubjID's and CondID's: CondIDs = []; for g = 1:length(handles.name.GroupID) GroupPath = [handles.paths.DataID,'/GroupID_',handles.name.GroupID{g}]; InputMats = dir([GroupPath,'/PreprocInputMEG_*.mat']); % Find InputMats for group InputMats = {InputMats.name}; SubjIDs = []; if ~isempty(InputMats) for m = 1:length(InputMats) LoadMat = load([GroupPath,'/',InputMats{m}]); SubjIDs = [SubjIDs; LoadMat.name.SubjID]; CondIDs = [CondIDs; cellstr(LoadMat.name.CurrentCondID)]; end else message = {['ERROR: PreprocInputMEG .mat file(s) missing for GroupID: ',... handles.name.GroupID{g}]; ''; ['As a result, some Subject & Condition IDs ' ... 'for the selected DataID may not be detected.']}; msgbox(message); end handles.name.SubjID{g} = unique(SubjIDs); end handles.name.CondID = unique(CondIDs); % Set output handles: OutputHandles = handles; %--- Compiles paths for selected indices: ---% %--------------------------------------------% function OutputHandles = CompilePaths(InputHandles, g, s, c) handles = InputHandles; handles.paths.InputPreprocMEG{g}{s,c} = ... [handles.paths.DataID,'/GroupID_',handles.name.GroupID{g},'/', ... handles.name.CondID{c},'/',handles.name.SubjID{g}{s},'_PreprocMEG.mat']; handles.paths.BackupOrigFile{g}{s,c} = ... [handles.paths.DataID,'/GroupID_',handles.name.GroupID{g},'/', ... handles.name.CondID{c},'/',handles.name.SubjID{g}{s},'_PreprocMEG_OrigBackup.mat']; % Set output handles: OutputHandles = handles; %==========================================% % FUNCTIONS FOR ADDING OR REMOVING GROUPS: % %==========================================% %--- Executes on selection change in ListboxGroupID. ---% %-------------------------------------------------------% function ListboxGroupID_Callback(hObject, eventdata, handles) if isempty(handles.name.GroupID) return; end handles = UpdateSubjIDSettings (handles); handles = UpdateStatusSettings (handles); guidata(hObject, handles); %--- Executes on button press in ButtonRmvGroupID. ---% %-----------------------------------------------------% function ButtonRmvGroupID_Callback(hObject, eventdata, handles) if isempty(handles.name.GroupID) return; end if length(handles.name.GroupID) <= 1 msgbox('Note: Must have at least 1 GroupID.', 'Error:'); return; end SelectedIndex = get(handles.ListboxGroupID, 'Value'); handles.name.GroupID(SelectedIndex) = []; if ~isempty(handles.name.SubjID) handles.name.SubjID(SelectedIndex) = []; end if ~isempty(handles.paths.InputPreprocMEG) handles.paths.InputPreprocMEG (SelectedIndex) = []; handles.paths.BackupOrigFile (SelectedIndex) = []; end if ~isempty(handles.gui.InpOutStatus) handles.gui.InpOutStatus(SelectedIndex) = []; end % Update settings & GUI: handles = UpdateGroupIDSettings (handles); handles = UpdateSubjIDSettings (handles); handles = UpdateStatusSettings (handles); % Save handles: guidata(hObject, handles); %--- Update GroupID settings & GUI panel: ---% %--------------------------------------------% function OutputHandles = UpdateGroupIDSettings(InputHandles) handles = InputHandles; % Update listbox: set(handles.ListboxGroupID, 'String', handles.name.GroupID); CurrentIndex = get(handles.ListboxGroupID, 'Value'); MaxGroupIndex = length(handles.name.GroupID); if isempty(CurrentIndex) || CurrentIndex == 0 || CurrentIndex > MaxGroupIndex set(handles.ListboxGroupID, 'Value', MaxGroupIndex); end % Set output handles: OutputHandles = handles; %--- Reset GroupID settings & GUI panel: ---% %-------------------------------------------% function OutputHandles = ResetGroupIDSettings(InputHandles) handles = InputHandles; handles.name.GroupID = []; set(handles.ListboxGroupID, 'String', []); set(handles.ListboxGroupID, 'Value', 1); % Set output handles: OutputHandles = handles; %============================================% % FUNCTIONS FOR ADDING OR REMOVING SUBJECTS: % %============================================% %--- Executes on selection change in ListboxSubjID. ---% %------------------------------------------------------% function ListboxSubjID_Callback(hObject, eventdata, handles) if isempty(handles.name.SubjID) return; end GroupIndex = get(handles.ListboxGroupID, 'Value'); if isempty(handles.name.SubjID{GroupIndex}) return; end SubjIndex = get(handles.ListboxSubjID, 'Value'); set(handles.ListboxInpOutStatus, 'Value', SubjIndex); %--- User enters subject to add into this textbox. ---% %-----------------------------------------------------% function TextboxSubjID_Callback(hObject, eventdata, handles) keypress = get(gcf,'CurrentCharacter'); if isequal(keypress, char(13)) ButtonAddSubjID_Callback(hObject, eventdata, handles); end %--- Executes on button press in ButtonAddSubjID. ---% %----------------------------------------------------% function ButtonAddSubjID_Callback(hObject, eventdata, handles) if isempty(handles.name.DataID) msgbox('Warning: Load DataID first.', 'Warning:'); return; end if isempty(handles.name.GroupID) msgbox('Error: No GroupIDs specified.', 'Error:'); return; end % Read entered SubjID: InputSubjID = get(handles.TextboxSubjID, 'String'); InputSubjID = deblank(InputSubjID); InputSubjID = regexprep(InputSubjID, '\s+', '_'); InputSubjID = cellstr(InputSubjID); if isempty(InputSubjID{1}) return; end GroupIndex = get(handles.ListboxGroupID, 'Value'); if ismember(InputSubjID{1}, handles.name.SubjID{GroupIndex}) return; % If SubjID already added end % Add SubjID for group & initialize: handles.name.SubjID{GroupIndex} = [handles.name.SubjID{GroupIndex}; InputSubjID]; NewSubjIndex = length(handles.name.SubjID{GroupIndex}); for c = 1:length(handles.name.CondID) handles = CompilePaths(handles, GroupIndex, NewSubjIndex, c); handles = DetectStatus(handles, GroupIndex, NewSubjIndex, c); end % Update settings & GUI: handles = UpdateSubjIDSettings (handles); handles = UpdateStatusSettings (handles); % Save handles: set(handles.ListboxSubjID, 'Value', NewSubjIndex); if ~isempty(handles.gui.InpOutStatus) set(handles.ListboxInpOutStatus, 'Value', NewSubjIndex); end guidata(hObject, handles); %--- Executes on button press in ButtonRmvSubjID. ---% %----------------------------------------------------% function ButtonRmvSubjID_Callback(hObject, eventdata, handles) if isempty(handles.name.SubjID) return; end GroupIndex = get(handles.ListboxGroupID, 'Value'); SubjIndex = get(handles.ListboxSubjID, 'Value'); if isempty(handles.name.SubjID{GroupIndex}) return; end handles.name.SubjID{GroupIndex}(SubjIndex) = []; if ~isempty(handles.paths.InputPreprocMEG) handles.paths.InputPreprocMEG {GroupIndex}(SubjIndex,:) = []; handles.paths.BackupOrigFile {GroupIndex}(SubjIndex,:) = []; end if ~isempty(handles.gui.InpOutStatus) handles.gui.InpOutStatus{GroupIndex}(SubjIndex,:) = []; end % Update settings & GUI: handles = UpdateSubjIDSettings (handles); handles = UpdateStatusSettings (handles); % Save handles: guidata(hObject, handles); %--- Update SubjID settings & GUI panel: ---% %-------------------------------------------% function OutputHandles = UpdateSubjIDSettings(InputHandles) handles = InputHandles; % Update listbox: GroupIndex = get(handles.ListboxGroupID, 'Value'); if isempty(handles.name.SubjID) set(handles.ListboxSubjID, 'String', []); MaxSubjIndex = 0; else set(handles.ListboxSubjID, 'String', handles.name.SubjID{GroupIndex}); MaxSubjIndex = length(handles.name.SubjID{GroupIndex}); end SubjIndex = get(handles.ListboxSubjID, 'Value'); if isempty(SubjIndex) || SubjIndex == 0 || SubjIndex > MaxSubjIndex set(handles.ListboxSubjID, 'Value', MaxSubjIndex); if ~isempty(handles.gui.InpOutStatus) set(handles.ListboxInpOutStatus, 'Value', MaxSubjIndex); end end % Set output handles: OutputHandles = handles; %--- Reset SubjID settings & GUI panel: ---% %------------------------------------------% function OutputHandles = ResetSubjIDSettings(InputHandles) handles = InputHandles; handles.name.SubjID = []; set(handles.ListboxSubjID, 'String', []); set(handles.ListboxSubjID, 'Value', 1); % Set output handles: OutputHandles = handles; %==============================================% % FUNCTIONS FOR ADDING OR REMOVING CONDITIONS: % %==============================================% %--- Executes on selection change in ListboxCondID. ---% %------------------------------------------------------% function ListboxCondID_Callback(hObject, eventdata, handles) if isempty(handles.name.CondID) return; end handles = UpdateStatusSettings(handles); guidata(hObject, handles); %--- User enters condition to add into this textbox. ---% %-------------------------------------------------------% function TextboxCondID_Callback(hObject, eventdata, handles) keypress = get(gcf,'CurrentCharacter'); if isequal(keypress, char(13)) ButtonAddCondID_Callback(hObject, eventdata, handles); end %--- Executes on button press in ButtonAddCondID. ---% %----------------------------------------------------% function ButtonAddCondID_Callback(hObject, eventdata, handles) if isempty(handles.name.DataID) msgbox('Warning: Load DataID first.', 'Warning:'); return; end if isempty(handles.name.GroupID) msgbox('Error: No GroupIDs specified.', 'Error:'); return; end % Read entered CondID: InputCondID = get(handles.TextboxCondID, 'String'); InputCondID = deblank(InputCondID); InputCondID = regexprep(InputCondID, '\s+', '_'); InputCondID = cellstr(InputCondID); if isempty(InputCondID{1}) return; end if ismember(InputCondID{1}, handles.name.CondID) return; % If CondID already added end % Add CondID and initialize: handles.name.CondID = [handles.name.CondID; InputCondID]; NewCondIndex = length(handles.name.CondID); for g = 1:length(handles.name.GroupID) if isempty(handles.name.SubjID) continue; end for s = 1:length(handles.name.SubjID{g}) handles = CompilePaths(handles, g, s, NewCondIndex); handles = DetectStatus(handles, g, s, NewCondIndex); end end set(handles.ListboxCondID, 'Value', NewCondIndex); % Update settings & GUI: handles = UpdateCondIDSettings (handles); handles = UpdateStatusSettings (handles); % Save handles: guidata(hObject, handles); %--- Executes on button press in ButtonRmvCondID. ---% %----------------------------------------------------% function ButtonRmvCondID_Callback(hObject, eventdata, handles) if isempty(handles.name.CondID) return; end SelectedIndex = get(handles.ListboxCondID, 'Value'); handles.name.CondID(SelectedIndex) = []; for g = 1:length(handles.name.GroupID) if ~isempty(handles.paths.InputPreprocMEG) handles.paths.InputPreprocMEG {g}(:,SelectedIndex) = []; handles.paths.BackupOrigFile {g}(:,SelectedIndex) = []; end if ~isempty(handles.gui.InpOutStatus) handles.gui.InpOutStatus{g}(:,SelectedIndex) = []; end end % Update settings & GUI: handles = UpdateCondIDSettings (handles); handles = UpdateStatusSettings (handles); % Save handles: guidata(hObject, handles); %--- Updates CondID settings & GUI panel: ---% %--------------------------------------------% function OutputHandles = UpdateCondIDSettings(InputHandles) handles = InputHandles; % Update listbox: set(handles.ListboxCondID, 'String', handles.name.CondID); CurrentIndex = get(handles.ListboxCondID, 'Value'); MaxCondIndex = length(handles.name.CondID); if isempty(CurrentIndex) || CurrentIndex == 0 || CurrentIndex > MaxCondIndex set(handles.ListboxCondID, 'Value', MaxCondIndex); end % Set output handles: OutputHandles = handles; %--- Reset CondID settings & GUI panel: ---% %------------------------------------------% function OutputHandles = ResetCondIDSettings(InputHandles) handles = InputHandles; handles.name.CondID = []; set(handles.ListboxCondID, 'String', []); set(handles.ListboxCondID, 'Value', 1); % Set output handles: OutputHandles = handles; %===============================% % FUNCTIONS FOR STATUS UPDATES: % %===============================% %--- Executes on selection change in ListboxInpOutStatus. ---% %------------------------------------------------------------% function ListboxInpOutStatus_Callback(hObject, eventdata, handles) if isempty(handles.gui.InpOutStatus) return; end if ~isempty(handles.name.GroupID) % In case where SubjID exist but CondID missing GroupIndex = get(handles.ListboxGroupID, 'Value'); if isempty(handles.gui.InpOutStatus{GroupIndex}) return; end end SelectedIndex = get(handles.ListboxInpOutStatus, 'Value'); set(handles.ListboxSubjID, 'Value', SelectedIndex); %--- Executes on button press in ButtonRefreshStatus. ---% %--------------------------------------------------------% function ButtonRefreshStatus_Callback(hObject, eventdata, handles) % Detect & update status: for g = 1:length(handles.name.GroupID) StatusWaitbar = waitbar(0, ['Please wait. Scanning status for: ', ... handles.name.GroupID{g}], 'Windowstyle', 'modal'); for s = 1:length(handles.name.SubjID{g}) waitbar(s/length(handles.name.SubjID{g}), StatusWaitbar); for c = 1:length(handles.name.CondID) handles = DetectStatus(handles, g, s, c); end end delete(StatusWaitbar) end handles = UpdateStatusSettings(handles); % Save handles: guidata(hObject, handles); %--- Executes on button press in CheckboxShowRemovedChannels. ---% %----------------------------------------------------------------% function CheckboxShowRemovedChannels_Callback(hObject, eventdata, handles) % Detect & update status: for g = 1:length(handles.name.GroupID) StatusWaitbar = waitbar(0, ['Please wait. Scanning status for: ', ... handles.name.GroupID{g}], 'Windowstyle', 'modal'); for s = 1:length(handles.name.SubjID{g}) waitbar(s/length(handles.name.SubjID{g}), StatusWaitbar); for c = 1:length(handles.name.CondID) handles = DetectStatus(handles, g, s, c); end end delete(StatusWaitbar) end handles = UpdateStatusSettings(handles); % Save handles: guidata(hObject, handles); %--- Detect input file status: ---% %---------------------------------% function OutputHandles = DetectStatus(InputHandles, g, s, c) handles = InputHandles; InputPreprocMEG = handles.paths.InputPreprocMEG{g}{s,c}; % Get input or output file status: if exist(InputPreprocMEG, 'file') LoadData = LoadFTmat(InputPreprocMEG, 'TrialCleaner'); if isempty(LoadData) StatusMsg = 'Error: PreprocMEG file found, but contains errors. See ErrorLog.'; elseif ~isfield(LoadData, 'NumTrialsRemoved') && ~isfield(LoadData, 'ChannelsRemoved') StatusMsg = 'PreprocMEG file found. Ready for cleaning.'; elseif get(handles.CheckboxShowRemovedChannels, 'Value') == 1 if isfield(LoadData, 'ChannelsRemoved') BadChanStr = []; for BadChan = 1:length(LoadData.ChannelsRemoved) if BadChan == 1 BadChanStr = LoadData.ChannelsRemoved{BadChan}; else BadChanStr = [BadChanStr,', ',LoadData.ChannelsRemoved{BadChan}]; end end StatusMsg = ['Channels removed: ',BadChanStr]; else StatusMsg = 'No channels removed.'; end else if isfield(LoadData, 'NumTrialsRemoved') NumBadTrls = num2str(LoadData.NumTrialsRemoved); else NumBadTrls = '0'; end if isfield(LoadData, 'ChannelsRemoved') NumBadChls = num2str(length(LoadData.ChannelsRemoved)); else NumBadChls = '0'; end StatusMsg = ['Trials removed: ',NumBadTrls,'. Channels removed: ',NumBadChls,'.']; end else StatusMsg = 'Error: PreprocMEG file could not be found.'; end handles.gui.InpOutStatus{g}{s,c} = StatusMsg; % Set output handles: OutputHandles = handles; %--- Update status settings & GUI panel: ---% %-------------------------------------------% function OutputHandles = UpdateStatusSettings(InputHandles) handles = InputHandles; % Make sure cells initialized: for g = 1:length(handles.name.GroupID) for s = 1:length(handles.name.SubjID{g}) for c = 1:length(handles.name.CondID) try handles.gui.InpOutStatus{g}{s,c}; % Check just in case. catch handles.gui.InpOutStatus{g}{s,c} = []; end end end end % Update listbox: GroupIndex = get(handles.ListboxGroupID, 'Value'); SubjIndex = get(handles.ListboxSubjID, 'Value'); CondIndex = get(handles.ListboxCondID, 'Value'); if isempty(handles.gui.InpOutStatus) || isempty(handles.gui.InpOutStatus{GroupIndex}) set(handles.ListboxInpOutStatus, 'String', []); set(handles.ListboxInpOutStatus, 'Value', 1); else set(handles.ListboxInpOutStatus, 'String', ... handles.gui.InpOutStatus{GroupIndex}(:,CondIndex)); if get(handles.ListboxInpOutStatus, 'Value') ~= SubjIndex set(handles.ListboxInpOutStatus, 'Value', SubjIndex); end end % Set output handles: OutputHandles = handles; %--- Reset status settings & GUI panel: ---% %------------------------------------------% function OutputHandles = ResetStatusSettings(InputHandles) handles = InputHandles; % Reset: handles.gui.InpOutStatus = []; % Reinitialize: for g = 1:length(handles.name.GroupID) for s = 1:length(handles.name.SubjID{g}) for c = 1:length(handles.name.CondID) handles.gui.InpOutStatus{g}{s,c} = []; end end end % Reset GUI listboxes: set(handles.ListboxInpOutStatus, 'String', []); set(handles.ListboxInpOutStatus, 'Value', 1); % Set output handles: OutputHandles = handles; %==========================================% % FUNCTIONS TO MARK BAD TRIALS & CHANNELS: % %==========================================% %--- Executes on button press in ButtonShowInstructions. ---% %-----------------------------------------------------------% function ButtonShowInstructions_Callback(hObject, eventdata, handles) Instructions = {'-------------'; 'INSTRUCTIONS:'; '-------------'; ''; 'LOADING INPUT DATA:'; ' 1) Load the DataID that you wish to apply ICA cleaning to.'; ' 2) The DataID will be scanned for Group, Subject & Condition IDs.'; ' 3) Use the "-" keys to exclude datasets from the ICA analysis.'; ' 4) Use the "+" keys to add missing Subject or Condition IDs.'; ''; ' NOTE: To restore the full list of Group, Subject, and Condition IDs'; ' found within the DataID, push the "Redetect Input" button.'; ''; ''; 'REMOVE BAD CHANNEL & TRIALS:'; ' 1) Change viewer settings as needed using the "Viewer Settings" button.'; ' 2) Select the Group, Subj & CondID of the dataset you wish to clean.'; ' 3) Push the "Mark Channels" button to view & mark bad channels.'; ' Push the "Mark Trials" button to view & mark bad trials.'; ''; ' Note: Once you quit the "RejectVisual" viewer, trials/channels marked'; ' as bad will automatically be removed from the dataset.'; ''; ''; 'BACKUP FILES & RESTORATION:'; ' - Before trials and/or channels are removed from a dataset, a BACKUP of'; ' the original dataset is automatically created.'; ''; ' - To UNDO the trial or channel removal for a specific dataset:'; ' 1) Select the Group, Subj & CondID of the dataset to restore.'; ' 2) Push the "Restore Dataset" button.'; ''; ''}; msgbox(Instructions, 'INSTRUCTIONS:') %--- Executes on button press in ButtonAdvSettings. ---% %------------------------------------------------------% function ButtonAdvSettings_Callback(hObject, eventdata, handles) if isempty(handles.paths.Rootpath) msgbox('Warning: Select target directory first.', 'Warning:'); return; end if isempty(handles.name.DataID) msgbox('Warning: Load DataID first.', 'Warning:'); return; end open([handles.paths.DataID,'/SETTINGS/Settings_RejectVisual.m']); %--- Executes on button press in ButtonRemoveChannels. ---% %---------------------------------------------------------% function ButtonRemoveChannels_Callback(hObject, eventdata, handles) if isempty(handles.name.DataID) msgbox('Warning: Load DataID first.', 'Warning:'); return; end if isempty(handles.name.GroupID) msgbox('Error: No GroupIDs specified.', 'Error:'); return; end g = get(handles.ListboxGroupID, 'Value'); s = get(handles.ListboxSubjID, 'Value'); c = get(handles.ListboxCondID, 'Value'); if isempty(handles.name.SubjID) || isempty(handles.name.SubjID{g}) msgbox('Error: No SubjIDs specified.', 'Error:'); return; end if isempty(handles.name.CondID) msgbox('Error: No CondIDs specified.', 'Error:'); return; end % Call viewer for user to mark bad channels: RmvBadChannelsTrials(handles, g, s, c, 'channel') % Detect & update status: handles = DetectStatus(handles, g, s, c); handles = UpdateStatusSettings(handles); % Save handles: guidata(hObject, handles); % Remove ErrorLog if empty: if exist([pwd,'/ErrorLog_TrialCleaner.txt'], 'file') LogCheck = dir('ErrorLog_TrialCleaner.txt'); if LogCheck.bytes == 0 delete('ErrorLog_TrialCleaner.txt') end end %--- Executes on button press in ButtonRemoveTrials. ---% %-------------------------------------------------------% function ButtonRemoveTrials_Callback(hObject, eventdata, handles) if isempty(handles.name.DataID) msgbox('Warning: Load DataID first.', 'Warning:'); return; end if isempty(handles.name.GroupID) msgbox('Error: No GroupIDs specified.', 'Error:'); return; end g = get(handles.ListboxGroupID, 'Value'); s = get(handles.ListboxSubjID, 'Value'); c = get(handles.ListboxCondID, 'Value'); if isempty(handles.name.SubjID) || isempty(handles.name.SubjID{g}) msgbox('Error: No SubjIDs specified.', 'Error:'); return; end if isempty(handles.name.CondID) msgbox('Error: No CondIDs specified.', 'Error:'); return; end % Call viewer for user to mark bad channels: RmvBadChannelsTrials(handles, g, s, c, 'trial') % Detect & update status: handles = DetectStatus(handles, g, s, c); handles = UpdateStatusSettings(handles); % Save handles: guidata(hObject, handles); % Remove ErrorLog if empty: if exist([pwd,'/ErrorLog_TrialCleaner.txt'], 'file') LogCheck = dir('ErrorLog_TrialCleaner.txt'); if LogCheck.bytes == 0 delete('ErrorLog_TrialCleaner.txt') end end %--- Calls viewer and housekeeping for channel & trial removal: ---% %------------------------------------------------------------------% function RmvBadChannelsTrials(InputHandles, g, s, c, RmvMode) handles = InputHandles; InputPreprocMEG = handles.paths.InputPreprocMEG{g}{s,c}; BackupOrigFile = handles.paths.BackupOrigFile{g}{s,c}; % Load input data & parameters: MEGdata = LoadFTmat(InputPreprocMEG, 'TrialCleaner'); if isempty(MEGdata) msgbox('Error: Input PreprocMEG file contains errors. See ErrorLog.') return; end CurrentDir = pwd; cd([handles.paths.DataID,'/SETTINGS/']); cfgReject = Settings_RejectVisual; cd(CurrentDir); cfgReject.method = RmvMode; % Viewing by 'channel' or 'trial' % Log extra [MEG]PLS fields (ft_rejectvisual will remove them by default): Backup = []; if isfield(MEGdata, 'AllEventInfo') Backup.AllEventInfo = MEGdata.AllEventInfo; end if isfield(MEGdata, 'ICAcompRemoved') Backup.ICAcompRemoved = MEGdata.ICAcompRemoved; end if isfield(MEGdata, 'ChannelsRemoved') Backup.ChannelsRemoved = MEGdata.ChannelsRemoved; end if isfield(MEGdata, 'NumTrialsRemoved') Backup.NumTrialsRemoved = MEGdata.NumTrialsRemoved; end % Make sure only relevant channels are shown in the viewer: % By default, all channels are shown and unselected channels are marked as bad. if strcmp(cfgReject.keepchannel, 'no') cfgSelect = []; cfgSelect.channel = cfgReject.channel; MEGdata = ft_selectdata_new(cfgSelect, MEGdata); end % Run "ft_rejectvisual": OrigChanList = MEGdata.label; % Log original channel list MEGdata = ft_rejectvisual(cfgReject, MEGdata) % Place extra [MEG]PLS fields back in: if isfield(Backup, 'AllEventInfo') MEGdata.AllEventInfo = Backup.AllEventInfo; end if isfield(Backup, 'ICAcompRemoved') MEGdata.ICAcompRemoved = Backup.ICAcompRemoved; end if isfield(Backup, 'ChannelsRemoved') MEGdata.ChannelsRemoved = Backup.ChannelsRemoved; end if isfield(Backup, 'NumTrialsRemoved') MEGdata.NumTrialsRemoved = Backup.NumTrialsRemoved; end % Detect & log removed channels & trials: if ~isequal(MEGdata.label, OrigChanList) BadChannels = OrigChanList(~ismember(OrigChanList, MEGdata.label)); if isfield(MEGdata, 'ChannelsRemoved') MEGdata.ChannelsRemoved = [MEGdata.ChannelsRemoved; BadChannels]; else MEGdata.ChannelsRemoved = BadChannels; end end if ~isempty(MEGdata.cfg.artifact) NumBadTrls = size(MEGdata.cfg.artifact, 1); if isfield(MEGdata, 'NumTrialsRemoved') MEGdata.NumTrialsRemoved = MEGdata.NumTrialsRemoved + NumBadTrls; else MEGdata.NumTrialsRemoved = NumBadTrls; end end % If channels / trials were removed, create backup & save: if isfield(MEGdata, 'ChannelsRemoved') || isfield(MEGdata, 'NumTrialsRemoved') if ~exist(BackupOrigFile, 'file') success = copyfile(InputPreprocMEG, BackupOrigFile, 'f'); if success == 0 msgbox('Warning: Failed to create backup of original file.', 'Warning:') end end CheckSavePath(InputPreprocMEG, 'TrialCleaner'); save(InputPreprocMEG, 'MEGdata'); end %--- Executes on button press in ButtonRestoreDataset. ---% %---------------------------------------------------------% function ButtonRestoreDataset_Callback(hObject, eventdata, handles) if isempty(handles.name.DataID) msgbox('Warning: Load DataID first.', 'Warning:'); return; end if isempty(handles.name.GroupID) msgbox('Error: No GroupIDs specified.', 'Error:'); return; end g = get(handles.ListboxGroupID, 'Value'); s = get(handles.ListboxSubjID, 'Value'); c = get(handles.ListboxCondID, 'Value'); if isempty(handles.name.SubjID) || isempty(handles.name.SubjID{g}) msgbox('Error: No SubjIDs specified.', 'Error:'); return; end if isempty(handles.name.CondID) msgbox('Error: No CondIDs specified.', 'Error:'); return; end % Check for backup file: InputPreprocMEG = handles.paths.InputPreprocMEG{g}{s,c}; BackupOrigFile = handles.paths.BackupOrigFile{g}{s,c}; if ~exist(BackupOrigFile, 'file') msgbox('Error: Backup PreprocMEG file could not be found.', 'Error:') return; end % Confirm restoration: prompt = {'WARNING:'; ''; 'Do you wish to RESTORE this dataset to its ORIGINAL state?'; 'This will undo any channel or trial removal previously done.'; ''}; Restore = questdlg(prompt, 'WARNING:', 'YES', 'NO', 'NO'); if strcmp(Restore, 'NO') return; end % Restore backup: if exist(BackupOrigFile, 'file') CheckSavePath(InputPreprocMEG, 'TrialCleaner'); success = movefile(BackupOrigFile, InputPreprocMEG, 'f'); if success == 0 msgbox('Error: Failed to restore to original PreprocMEG file.', 'Error:') end end % Detect & update status: handles = DetectStatus(handles, g, s, c); handles = UpdateStatusSettings(handles); % Save handles: guidata(hObject, handles); %--- Executes on button press in ButtonRestoreAll. ---% %-----------------------------------------------------% function ButtonRestoreAll_Callback(hObject, eventdata, handles) if isempty(handles.name.DataID) msgbox('Warning: Load DataID first.', 'Warning:'); return; end if isempty(handles.name.GroupID) msgbox('Error: No GroupIDs specified.', 'Error:'); return; end if isempty(handles.name.SubjID) msgbox('Error: No SubjIDs specified.', 'Error:'); return; else g = get(handles.ListboxGroupID, 'Value'); if isempty(handles.name.SubjID{g}) msgbox('Error: No SubjIDs added for selected Group.', 'Error:') return; end end if isempty(handles.name.CondID) msgbox('Error: No CondIDs specified.', 'Error:'); return; end % Confirm restoration: prompt = {'WARNING:'; ''; 'This will RESTORE ALL current datasets to their BACKUP state.'; 'Any channel or trial removal previously done will be reverted.'; 'Do you wish to continue?'; ''; 'Note: Only datasets with backup file states will be restored.'; ' If a backup file is not found, the dataset will be skipped.'; ''}; Restore = questdlg(prompt, 'WARNING:', 'YES', 'NO', 'NO'); if strcmp(Restore, 'NO') return; end % Restore all datasets to original state: for g = 1:length(handles.name.GroupID) for s = 1:length(handles.name.SubjID{g}) for c = 1:length(handles.name.CondID) InputPreprocMEG = handles.paths.InputPreprocMEG{g}{s,c}; BackupOrigFile = handles.paths.BackupOrigFile{g}{s,c}; if exist(BackupOrigFile, 'file') CheckSavePath(InputPreprocMEG, 'TrialCleaner'); success = movefile(BackupOrigFile, InputPreprocMEG, 'f'); if success == 1 StatusMsg = 'Backup PreprocMEG file restored successfully.'; else StatusMsg = 'Error: Failed to restore backup PreprocMEG file.'; end else StatusMsg = 'Warning: No backup PreprocMEG file found. Skipped restore.'; end handles.gui.InpOutStatus{g}{s,c} = StatusMsg; end % Cond end % Subj end % Group % Update status: handles = UpdateStatusSettings(handles); % Save handles: guidata(hObject, handles); %=================================% % FUNCTION TO OPEN MODAL WAITBOX: % %=================================% function WaitBox = StartWaitBox(TextString) WaitBox = dialog('Units', 'pixels', 'Position', [500 500 300 40],... 'Windowstyle', 'modal', 'NextPlot', 'new', 'Name', 'Please Wait:'); uicontrol('Parent', WaitBox, 'Units', 'pixels', 'Position', [20 10 250 20],... 'Style', 'text', 'String', TextString, 'FontWeight', 'bold'); movegui(WaitBox, 'center'); %============================% % GUIDE CREATEFCN FUNCTIONS: % %============================% % --- Executes during object creation, after setting all properties. function TextboxRootpath_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes during object creation, after setting all properties. function TextboxDataID_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes during object creation, after setting all properties. function ListboxGroupID_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes during object creation, after setting all properties. function ListboxSubjID_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes during object creation, after setting all properties. function TextboxSubjID_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes during object creation, after setting all properties. function ListboxCondID_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes during object creation, after setting all properties. function TextboxCondID_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes during object creation, after setting all properties. function ListboxInpOutStatus_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end